package de.uni.bremen.estudent.lbs.util;

import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;

/**
 * 
 * @author bahlfeld
 *
 */
public final class LocationRequester implements LocationListener {
	
	private static final String TAG = "eStudentLBS - LocationRequester";
	
	private static final int GPS_PROVIDER_UPDATE_DISTANCE = 10;
	/**
	 * Updatetime for gps provider in millisec.
	 */
	private static final int GPS_PROVIDER_UPDATE_TIME = 60000;
	private static final int GPS_STALE_TIME = 30000;
	private static final int NETWORK_PROVIDER_UPDATE_DISTANCE = 20;
	/**
	 * Updatetime for network provider in millisec.
	 */
	private static final int NETWORK_PROVIDER_UPDATE_TIME = 12000;
	
	/**
	 * Current location.
	 */
	private Location mCurrentLocation;
	/**
	 * Locationmanager object.
	 */
	private LocationManager mLocationManager;
	/**
	 * location listener object.
	 */
	private LocationListener mLocationListener;
	
	
	public LocationRequester(Context context){
		mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
		requestUpdates();
	}
	
	/**
	 * Locationrequester which notifies a locationlistener if a new position is available.
	 * @param context
	 * @param locationListener
	 */
	public LocationRequester(Context context, LocationListener locationListener){
		mLocationListener = locationListener;
		mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
		requestUpdates();
	}
	
	
	private void requestUpdates() {
		mCurrentLocation = getLastLocationFromProvider(LocationManager.GPS_PROVIDER);
		if(mCurrentLocation == null){
			mCurrentLocation = getLastLocationFromProvider(LocationManager.NETWORK_PROVIDER);
		}
		
		if(mCurrentLocation != null){
			mLocationListener.onLocationChanged(mCurrentLocation);
		}
		mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, GPS_PROVIDER_UPDATE_TIME, GPS_PROVIDER_UPDATE_DISTANCE, this);
		mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,NETWORK_PROVIDER_UPDATE_TIME,NETWORK_PROVIDER_UPDATE_DISTANCE,this);
	}


	/**
	 * Returns the last known location if it is not older then GPS_STALE_TIME.
	 * @param networkProvider
	 * @return
	 */
	private Location getLastLocationFromProvider(String networkProvider) {
		Location lastKnownLocation = mLocationManager.getLastKnownLocation(networkProvider);
		if(lastKnownLocation == null){
			Log.e(TAG, "Last known location is null");
			return null;
		}
		if((System.currentTimeMillis() - lastKnownLocation.getTime()) > GPS_STALE_TIME){
			Log.e(TAG, "Last known location is to old.");
			return null;
		}
		Log.i(TAG,"Lastknown location: lon: " + lastKnownLocation.getLongitude() + " lat: " + lastKnownLocation.getLatitude());
		return lastKnownLocation;
	}


	/**
	 * Only set the location, if it comes from the gps module or is not older then the GPS_STALE_STATE.
	 */
	@Override
	public void onLocationChanged(Location location) {
		if(mCurrentLocation == null){
			setLocation(location);
		} else if(location.getProvider().equals(LocationManager.GPS_PROVIDER)){
			setLocation(location);
		} else if(mCurrentLocation.getTime() - location.getTime() > GPS_STALE_TIME){
			setLocation(location);
		}
	}

	private void setLocation(Location location) {
		mCurrentLocation = location;
		Log.i(TAG,"Set location to " + location.toString());
		if(mLocationListener != null){
			Log.i(TAG, "Notify registered locationlistener.");
			mLocationListener.onLocationChanged(location);
		}
	}


	@Override
	public void onProviderDisabled(String provider) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onProviderEnabled(String provider) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
		// TODO Auto-generated method stub

	}

	/**
	 * Restarts the listing for location. Call this method in the onResume of an activity or 
	 * a fragment.
	 */
	public void restart(){
		this.requestUpdates();
	}
	
	/**
	 * Stops the listening for new updates. Call this methode in the onPause of an activity or a
	 * fragment!
	 */
	public void stop(){
		mLocationManager.removeUpdates(mLocationListener);
	}
	
	/**
	 * Returns the current location. Can be null.
	 * @return
	 */
	public Location getLocation(){
		return mCurrentLocation;
	}
}
